
#include "amf.h"
#include "byte_process.h"
#include <cstdio>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sstream>

AmfKey::AmfKey()
{
    m_key_len = 0;
}

int AmfKey::ParseAmfKey(const uint8_t *data, int length)
{
    int index = 0;
    m_key_len = BeToH16(data, length);
    /* printf("object key len:%d.\n", m_key_len); */
    index = 2;
    int i = 0;
    for (; index < m_key_len + 2 && i < length; i++)
    {
        if ((*(data + i) >= 'A' && *(data + i) <= 'Z') || (*(data + i) >= 'a' && *(data + i) <= 'z'))
        {
            m_key_value += *(data + i);
            index++;
        }
        else if (*(data + i) >= '0' && *(data + i) <= '9')
        {
            m_key_value += *(data + i);
            index++;
        }
    }
    /* printf("index : %d\t %d.\n", i,index); */
    return i;
}

int AmfKey::CopyBuffer(uint8_t *data, int length)
{
    m_key_len = m_key_len == 0 ? m_key_value.size() : m_key_len;

    int index = 0;
    uint16_t len = htobe16(m_key_len);

    memcpy(data, &len, 2);
    index += 2;

    memcpy(data + index, m_key_value.c_str(), m_key_len);

    return index + m_key_len;
}


AmfValue *AmfValue::CreateAmfValueByType(VALUE_TYPE type)
{
    AmfValue * value = nullptr;
    switch (type)
    {
        case NUMBER:
        {
            value = new AmfNumber();
            break;
        }
        case BOOLEAN:
        {
            value = new AmfBoolean();
            break;
        }
        case STRING:
        {
            value = new AmfString();
            break;
        }
        case NULL_TYPE:
        {
            value = new AmfNull();
            break;
        }
        case ARRAY_TYPE:
        {
            value = new AmfArray();
            break;
        }
        case START_OBJECT:
        {
            value = new AmfObject();
            break;
        }
        default:
            break;
    }
    if (value != nullptr)
    {
        value->m_type = type;
    }
    return value;
}

AmfValue *AmfValue::ParseBuffer(const uint8_t *data, int length, int & index)
{
    index = 0;
    AmfValue * value = CreateAmfValueByType((VALUE_TYPE)(data[index]));
    if (value != nullptr)
    {
        index = value->ParseValueFromBuffer(data, length);
    }
    if (index == -1)
    {
        // 解析不完全
        delete value;
        value = nullptr;
    }
    return value;
}

///>number type/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AmfNumber::AmfNumber()
{

}

int AmfNumber::ParseValueFromBuffer(const uint8_t *data, int length)
{
    int index = 1;
    uint64_t tmp = BeToH64(data + index, length);
    memcpy(&m_value, &tmp, 8);
    /* m_value = tmp; */
    return index + 8;
}

int AmfNumber::CopyToBuffer(uint8_t *data, int length)
{
    int index = 0;
    data[index] = NUMBER;

    index++;

    uint64_t tmp = 0;
    memcpy(&tmp, &m_value, 8);

    tmp = HToBe64((uint8_t*)&tmp, 8);
    memcpy(data + index, &tmp, sizeof(uint64_t));

    return 9;
}

int AmfNumber::GetLength()
{
    return 9;
}


///>boolean type/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AmfBoolean::AmfBoolean()
{
    m_value = false;
}

int AmfBoolean::ParseValueFromBuffer(const uint8_t *data, int length)
{
    int index = 1;
    m_value = data[index];

    return index + 1;
}

int AmfBoolean::CopyToBuffer(uint8_t *data, int length)
{
    int index = 0;
    data[index] = BOOLEAN;

    index++;
    data[index] = m_value;

    return index + 1;
}

int AmfBoolean::GetLength()
{
    return 2;
}

void AmfBoolean::SetValue(bool value)
{
    m_value = value;
}

///>short string type//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AmfString::AmfString()
{

}

int AmfString::ParseValueFromBuffer(const uint8_t *data, int length)
{
    int index = 1;
    uint16_t short_string_length = BeToH16(data + index, length - index);
    // 65535 limit
    m_value.assign((char*)(data + index + 2), short_string_length);
    index += 2 + short_string_length;

    return index;
}

int AmfString::CopyToBuffer(uint8_t *data, int length)
{
    int index = 0;
    data[index++] = STRING;

    uint16_t  string_length = htobe16(m_value.size());
    memcpy(data + index, &string_length, 2);

    index += 2;

    memcpy(data + index, m_value.c_str(), m_value.size());

    return index + m_value.size();
}

int AmfString::GetLength()
{
    return 3 + m_value.size(); 
}

///>null type//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AmfNull::AmfNull()
{

}

int AmfNull::ParseValueFromBuffer(const uint8_t *data, int length)
{
    return 1;
}

int AmfNull::CopyToBuffer(uint8_t *data, int length)
{
    data[0] = NULL_TYPE;
    return 1;
}

int AmfNull::GetLength()
{
    return 1;
}

///>array type//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AmfArray::AmfArray()
{

}

int AmfArray::ParseValueFromBuffer(const uint8_t *data, int length)
{
    int index = 1;
    uint16_t array_len = BeToH16(data + index, length - index);

    index += 2;

    int array_index = 0;
    for (array_index = 0; array_index < array_len && index < length; array_index++)
    {
        AmfKey key;
        index += key.ParseAmfKey(data + index, length - index);

        int value_len = 0;
        AmfValue * value = ParseBuffer(data + index, length - index, value_len);
        index += value_len;

        m_key_vec.push_back(key);
        m_value_vec.push_back(value);
    }
    if (array_index == array_len)
    {

    }
    else
    {
        for (auto iter = m_value_vec.begin(); iter != m_value_vec.end(); )
        {
            delete (*iter);
            iter = m_value_vec.erase(iter);
        }
        m_value_vec.clear();
    }
    return m_value_vec.size() ? index : -1;
}

int AmfArray::CopyToBuffer(uint8_t *data, int length)
{
    int index = 0;
    data[index++] = ARRAY_TYPE;
    for (int i = 0; i < m_key_vec.size(); i++)
    {
        index += m_key_vec[i].CopyBuffer(data + index, length - index);
        index += m_value_vec[i]->CopyToBuffer(data + index, length - index);
    }
    return index;
}

std::string AmfArray::Dump()
{
    std::stringstream ss;
    for (auto & item : m_key_vec)
    {
        ss << "key:" << item.m_key_value << ";";
    }
    return ss.str();
}

int AmfArray::GetLength()
{
    int length = 0;
    for (auto & data : m_key_vec)
    {
        length += data.m_key_value.size() + 2;        
    }
    for (auto & data : m_value_vec)
    {
        length += data->GetLength();
    }
    return length;
}

///>object type//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AmfObject::AmfObject()
{

}

AmfObject::~AmfObject()
{
    for (auto & data : m_value_map)
    {
        delete data.second;
    }
    m_value_map.clear();
}

int AmfObject::ParseValueFromBuffer(const uint8_t *data, int length)
{
    int index = 1;
    uint32_t end_data = 0x00090000;
    uint8_t * end_object = (uint8_t*)((&end_data));

    bool has_end = false;
    for (; index < length;)
    {
        AmfKey key;
        int len = key.ParseAmfKey(data + index, length - index);
        index += len;
        int value_len = 0;
        AmfValue * value = ParseBuffer(data + index, length - index, value_len);
        index += value_len;
        m_value_map[key.m_key_value] = value;
        if (memcmp(data + index, end_object, 3) == 0)
        {
            has_end = true;
            break;
        }
    }
    if (has_end)
    {
        index += 3;
    }
    else
    {
        for (auto & item : m_value_map)
        {
            delete item.second;
        }
        m_value_map.clear();
    }
    return has_end ? index : -1;
}

int AmfObject::CopyToBuffer(uint8_t *data, int length)
{
    int index = 0;
    data[index++] = START_OBJECT;
    for (auto & item : m_value_map)
    {
        AmfKey key;
        key.m_key_len   = item.first.size();
        key.m_key_value = item.first;
        index += key.CopyBuffer(data + index, length - index);
        /* printf("compose object data index value : %d.\n", index); */
        index += item.second->CopyToBuffer(data + index, length - index);

        /* printf("compose object data index value1 : %d.\n", index); */
    }
    data[index] = 0x00;
    data[index + 1] = 0x00;
    data[index + 2] = 0x09;

    return index + 3;
}

std::string AmfObject::Dump()
{
    std::stringstream ss;
    for (auto & data : m_value_map)
    {
        ss << "key:" << data.first << ";";
    }
    return ss.str();
}

int AmfObject::GetLength()
{
    int length = 0;
    for (auto & data : m_value_map)
    {
        length += 2 + data.first.size();
        length += data.second->GetLength();
    }
    length += 1;    // start object;
    length += 3;
    return length;
}

void AmfObject::SetBool(const std::string & key, bool value)
{
    auto boolean_value = new AmfBoolean();
    boolean_value->SetValue(value);
    m_value_map[key] = boolean_value;
}

void AmfObject::SetNumber(const std::string & key, double number)
{
    auto number_value = new AmfNumber();
    number_value->SetValue(number);
    m_value_map[key] = number_value;

}

void AmfObject::SetString(const std::string & key, const std::string & value)
{
    auto value_str = new AmfString();
    value_str->SetString(value);
    m_value_map[key] = value_str;
}

void AmfObject::SetObject(const std::string & key, AmfObject * object)
{
    m_value_map[key] = object;
}


